package com.taikang.base;


import com.taikang.base.entity.BaseBusLongEntity;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Properties;

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class BaseEntityInterceptor implements Interceptor {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];

        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();

        final Object parameter = invocation.getArgs()[1];

        if (!ClassUtils.isAssignable(BaseBusLongEntity.class, parameter.getClass())) {
            return invocation.proceed();
        }

        if (AdminUserHolder.getCurrentUser().isPresent()) {

            //set create by when insert.
            if (SqlCommandType.INSERT == sqlCommandType) {
                ReflectionUtils.doWithFields(parameter.getClass(), new ReflectionUtils.FieldCallback() {
                    @Override
                    public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                        ReflectionUtils.makeAccessible(field);
                        logger.debug("set {} with user name {}", field.getName(), AdminUserHolder.getCurrentUser().get().getName());
                        field.set(parameter, AdminUserHolder.getCurrentUser().get().getName());
                    }
                }, new ReflectionUtils.FieldFilter() {
                    @Override
                    public boolean matches(Field field) {
                        return field.getName().equals(BaseBusLongEntity.CREATE_BY_PROPERTY_NAME)
                                || field.getName().equals(BaseBusLongEntity.UPDATE_BY_PROPERTY_NAME);
                    }
                });
            } else if (SqlCommandType.UPDATE == sqlCommandType) {
                //set update by when insert and update.
                ReflectionUtils.doWithFields(parameter.getClass(), new ReflectionUtils.FieldCallback() {
                    @Override
                    public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                        ReflectionUtils.makeAccessible(field);
                        logger.debug("set {} with user name {}", field.getName(), AdminUserHolder.getCurrentUser().get().getName());
                        field.set(parameter, AdminUserHolder.getCurrentUser().get().getName());
                    }
                }, new ReflectionUtils.FieldFilter() {
                    @Override
                    public boolean matches(Field field) {
                        return field.getName().equals(BaseBusLongEntity.UPDATE_BY_PROPERTY_NAME);
                    }
                });
            }
        }

        //update at
        ReflectionUtils.doWithFields(parameter.getClass(), new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                ReflectionUtils.makeAccessible(field);
                logger.debug("set {} with current time", field.getName());
                field.set(parameter, new Date());
            }
        }, new ReflectionUtils.FieldFilter() {
            @Override
            public boolean matches(Field field) {
                return field.getName().equals(BaseBusLongEntity.UPDATE_DATE_PROPERTY_NAME);
            }
        });

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
